#客户管理相关内容

from django.http import JsonResponse
from django.shortcuts import render,HttpResponse,redirect
from django.urls import reverse
from utils.bootstrap import BootStrapForm
from utils.encrypt import md5
from utils.group import NbSearchGroup, get_search_group_condition,Option
from utils.link import filter_reverse
from utils.reponse import BaseResponse
from web import models
from django import forms
from django.core.exceptions import ValidationError, BadRequest
import re
from django.db.models import Q
from utils.pager import Pagination
#客户列表显示
def customer_list(request):
    #优化：添加组合搜索功能
    search_group = NbSearchGroup(request,models.Customer,
    Option("level",is_multi=True, text_func=lambda x: x.title,
                                      value_func=lambda x: x.id,db_condition={"active":1})
                                 )
    # 产生搜索条件：
    conn = search_group.get_condition
    #优化：添加查找功能
    keyword = request.GET.get("keyword","").strip() #获取前端提交需要查找的数据
    # 以Q方法查找内容实现
    q1 = Q()
    if keyword:
        q1.connector = 'OR'
        #不同字段的关键字搜索 包含条件
        q1.children.append(('username__contains', keyword))
        q1.children.append(('mobile__contains', keyword))
        q1.children.append(('level__title__contains', keyword))

    # 1 获取激活的客户列表 level__active跨表查询级别为1  select_related( )：需要用到跨表时主动跨表 防止后面重复跨表查询
    queryset = models.Customer.objects.filter(active=1).filter(q1).filter(**conn).select_related("level","creator" )
    # 2 分页优化，防止数据太多分页展示 http://localhost:8000/customer/list/?page=1  page第几页
    obj = Pagination(request = request,query_set = queryset) #实例化页码类
    pager_string = obj.html() #生成html代码的页码函数
    context = {
        "queryset": queryset[obj.start:obj.end],  # 切片显示前5个数据
        "pager_string": pager_string,
        "keyword": keyword, #把搜索内容给到前端做显示，看见搜的是什么
        "search_group": search_group
    }
    return render(request, "customer_list.html",context)


# 客户列表添加的Form 继承BootStrapForm通用form样式
class CustomerModelForm(BootStrapForm,forms.ModelForm):
    exclude_filed_list = ["level"] #排除level字段，不需要加样式 方便后面自定义样式
    #添加一个密码的字段，用于密码的重复验证  widget样式
    # widget= 常见的有 forms.PasswordInput ;forms.TextInput ; forms.Select(下拉框），
    # forms.RadioSelect(单选框）;forms.CheckboxInput(多选框）；forms.Textarea（多行文本）
    confirm_password = forms.CharField(label="重复密码",
                                       widget=forms.PasswordInput(render_value=True)) #render_value保留历史值

    #验证手机号格式
    # mobile = forms.CharField(
    #     label="手机号",
    #     validators=[RegexValidator(r'^1[358]\d{9}$', '手机号需要是13or5or8开头的11位数字')]
    # )

    class Meta:
        model = models.Customer #models.表名 【固定格式，必须有】
        fields = ["mobile","username","password","confirm_password","level"]
        widgets= {
            "password":forms.PasswordInput,#显示密码隐藏
            "level":forms.RadioSelect(attrs={"class":"form-radio"})  #把下拉框变成单选框 并且加一个样式 form-radio 在commons.css自定义样式
        }
    #处理界面显示"**VIP级别表**"时显示所有，但是没有去除逻辑删除（"active"=0）的数据情况
    def __init__(self,request,*args,**kwargs):
        super().__init__(*args,**kwargs)
        # dat = request.GET.get() #获取request的数据，可以作为条件限制 与请求相关有不用结果时使用
        #self.fields所有的字段 queryset默认查询的就是 level数据库表中与之关联的所有数据
        # models.Level.objects.filter(active=1) 把之前关联的修改为自定义的 active=1是条件
        self.fields["level"].queryset = models.Level.objects.filter(active=1)

    # 钩子方法校验手机号
    # def clean_mobile(self):
    #     mobile =  self.cleaned_data.get("mobile")
    #     if not re.match(r'^1[358]\d{9}$',mobile):
    #         raise ValidationError('手机号需要是13or5or8开头的11位数字')
    #     return mobile

    # 解决密码没加密就存到数据库(钩子方法）
    def clean_password(self):
        password = self.cleaned_data.get("password")
        return md5(password)
    #两次密码一致性校验 之前普通密码做了md5加密传输 确认密码也需要MD5加密后比较
    def clean_confirm_password(self):
        confirm_password = self.cleaned_data.get("confirm_password")
        password = self.cleaned_data.get("password") # confirm_password在password后面所有能够拿到数据
        if md5(confirm_password) != password:
            raise ValidationError('两次密码不一致')
        return confirm_password

#客户列表添加(form2.html)
def customer_add(request):
    if request.method == "GET":
        form = CustomerModelForm(request)
        # form2 一行放2个输入框
        return render(request, "form2.html", {"form": form})
    # 获取数据 校验 并且更新数据
    form = CustomerModelForm(request = request,data=request.POST) #传入一个 instance = 数据库对象，没有instance就是新增
    if not form.is_valid():
        return render(request, 'form2.html', {"form": form})
    #添加客户是谁创建的 使用当前登录账户的id
    form.instance.creator_id = request.nb_user.id
    #解决密码没加密就存到数据库
    # pwd = form.cleaned_data["password"]
    # form.instance.password = md5(pwd)
    #自动更新
    form.save()
    return redirect(reverse('customer_list'))

# 客户列表编辑的Form 继承BootStrapForm通用form样式
class CustomerEditModelForm(BootStrapForm,forms.ModelForm):
    exclude_filed_list = ["level"] #排除level字段，不需要加样式 方便后面自定义样式


    class Meta:
        model = models.Customer #models.表名
        fields = ["mobile","username","level"]
        widgets= {
            "password":forms.PasswordInput,#显示密码隐藏
            "level":forms.RadioSelect(attrs={"class":"form-radio"})  #把下拉框变成单选框 并且加一个样式 form-radio 在commons.css自定义样式
        }
    #处理界面显示"**VIP级别表**"时显示所有，但是没有去除逻辑删除（"active"=0）的数据情况
    def __init__(self,request,*args,**kwargs):
        super().__init__(*args,**kwargs)
        # dat = request.GET.get() #获取request的数据，可以作为条件限制 与请求相关有不用结果时使用
        #self.fields所有的字段 queryset默认查询的就是 level数据库表中与之关联的所有数据
        # models.Level.objects.filter(active=1) 把之前关联的修改为自定义的 active=1是条件
        self.fields["level"].queryset = models.Level.objects.filter(active=1)




# 编辑客户列表 只能编辑用户名 手机号 级别
def customer_edit(request,pk):
    #获取数据库的默认值
    instance =  models.Customer.objects.filter(id = pk,active=1).first()
    if request.method == "GET":
        # instance= instance 展示默认值
        form = CustomerEditModelForm(request,instance= instance)
        # form2 一行放2个输入框
        return render(request, "form2.html", {"form": form})

    form = CustomerEditModelForm(request,data=request.POST,instance= instance)
    if not form.is_valid():
        return render(request, 'form2.html', {"form": form})
    form.save()
    #优化 ：修改数据后跳转回之前的界面
    url = filter_reverse(request,reverse('customer_list')) #函数方法实现
    '''
    源代码
    # 1 获取get参数 filter
    # filter_string = request.GET.get("_filter")
    # print("filter_string", filter_string)
    # if not filter: #没有直接返回
    #     return redirect(reverse('customer_list'))
    # #有
    # base_url = reverse("customer_list")
    # url = "{}?{}".format(base_url, filter_string)
    # print(url)
    '''

    return redirect(url)


# 客户列表重置密码
class CustomerResetModelForm(BootStrapForm,forms.ModelForm):
    confirm_password = forms.CharField(label="重复密码",
                                       widget=forms.PasswordInput(render_value=True)) #render_value保留历史值
    class Meta:
        model = models.Customer #models.表名
        fields = ["password","confirm_password"]
        widgets= {
            "password":forms.PasswordInput(render_value=True),#显示密码隐藏
        }


    # 解决密码没加密就存到数据库(钩子方法）
    def clean_password(self):
        password = self.cleaned_data.get("password")
        return md5(password)
    #两次密码一致性校验 之前普通密码做了md5加密传输 确认密码也需要MD5加密后比较
    def clean_confirm_password(self):
        confirm_password = self.cleaned_data.get("confirm_password")
        password = self.cleaned_data.get("password") # confirm_password在password后面所有能够拿到数据
        if md5(confirm_password) != password:
            raise ValidationError('两次密码不一致')
        return confirm_password
#重置密码
def customer_reset(request,pk):
    if request.method == "GET":
        # instance= instance 展示默认值
        form = CustomerResetModelForm()
        # form2 一行放2个输入框
        return render(request, "form2.html", {"form": form})
    # 获取数据库的默认值
    instance = models.Customer.objects.filter(id=pk, active=1).first()
    form = CustomerResetModelForm(data=request.POST, instance=instance)
    if not form.is_valid():
        return render(request, 'form2.html', {"form": form})
    form.save()
    return redirect(reverse('customer_list'))

# 删除客户列表中的一个数据
def customer_delete(request):
    print("调用删除方法")
    cid = request.GET.get("cid")
    print("需要删除", cid)
    #异常判断 如果为空
    if not cid:
        res = BaseResponse(status=False,detail="请选中需要删除的内容")
        return JsonResponse(res.dict)
    # 异常判断 数据库里面没有
    if not models.Customer.objects.filter(id=cid, active=1).exists():
        res = BaseResponse(status=False, detail="数据不存在")
        return JsonResponse(res.dict)
    #存在
    #使用之前定义的ajax返回值使用的类
    res = BaseResponse(status=True)
    models.Customer.objects.filter(id=cid, active=1).update(active=0)
    return JsonResponse(res.dict)



# 充值功能：交易记录实现 的forms
class ChargeModelForm(BootStrapForm,forms.ModelForm):
    # 方式1重新字段：重写交易的类型字段，只显示扣款与充值 【只适合固定的数据，不适合数据库表中获取数据】
    # charge_type = forms.TypedChoiceField(
    #     label= "交易类型", #标签名字
    #     choices=[(1,"充值"),(2,"扣款")],
    #     coerce=int, #把获取的数据自动转换成int
    # )
    class Meta:
        model = models.TransactionRecord #交易记录表字段
        fields = ["charge_type","amount"] #交易类型，金额

    # 方式2 构造方法实现：重写交易的类型字段，只显示扣款与充值【数据库添加，会实时变化】
    # choice会自动校验传过来的值是不是属于1，2
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        self.fields["charge_type"].choices = [(1,"充值"),(2,"扣款")]


# 充值功能：交易记录实现
def customer_charge(request,pk):
    # 获取数据库的默认值,读取交易记录表 [客户id,链表查询客户存在,交易记录本身存在】,倒叙排序
    queryset = models.TransactionRecord.objects.filter(customer_id=pk,customer__active=1,active=1).order_by("-id")
    pager = Pagination(request=request, query_set=queryset)  # 实例化页码类
    form =  ChargeModelForm()
    #分页
    # print(locals()) #获取函数中所有的局部变量，构成字典{request：，pk：xxxx}
    # return render(request, "customer_charge.html", {"pager":pager,"form":form,'pk':pk})
    return render(request, "customer_charge.html", locals()) #效果同上

# 充值功能：ajax提交的交易数据地址
def customer_charge_add(request,pk):
    print("ajax提交的交易数据地址")
    print(pk) #客户ID
    print(request.POST) #充值记录【类型，金额】
    #数据校验
    form = ChargeModelForm(data=request.POST)
    #失败
    if not form.is_valid():
        # 数据校验失败返回所有错误信息
        res = BaseResponse(status=False,detail=form.errors)
        return JsonResponse(res.dict)
    from django.db import transaction
    # 成功
    amount = form.cleaned_data["amount"]  # 充值、减少 金额
    charge_types = form.cleaned_data["charge_type"]  # 充值还是扣款
    try:
    # 事务操作atomic：保证两个数据表的操作【账户数据表】【交易记录数据表】成功都成功，失败就回滚 不会出现一个成功【账户充值了】
    # 一个失败【交易记录没有出现】问题
        with transaction.atomic():
            # 1、对当前用户更新账户数据;函数select_for_update() 为数据库加锁【排他锁】
            cus_object = models.Customer.objects.filter(id=pk, active=1).select_for_update().first()  # 找到客户对象
            #检查 数据库的余额、充值金额的 数据类型 都是返回decimal.Decimal 可以比较，加减同浮点数操作
            print(type(cus_object.balance),type(amount))
            # 判断余额是否够，保证数据不是负数
            if charge_types == 2 and (cus_object.balance < amount):
                res = BaseResponse(status=False,detail = {"amount":[f"余额不足，账户还有{cus_object.balance}"]})
                return JsonResponse(res.dict)

            # 类型判断是充值还是扣费
            if charge_types == 1:
                cus_object.balance += amount #添加金额
            else:
                cus_object.balance -= amount #减少金额
            cus_object.save() #更新数据
            #2产生交易记录 form：交易记录表
            form.instance.customer = cus_object # 在交易记录表加 客户对象
            form.instance.creator_id = request.nb_user.id # 交易记录表加 管理员id
            form.save() #更新数据
    except Exception as e:
        res = BaseResponse(status=False,detail="操作失败")
        return JsonResponse(res.dict)

    res = BaseResponse(status=True)
    return JsonResponse(res.dict)